/*
 * Copyright (c) 2018-2999 广州亚米信息科技有限公司 All rights reserved.
 *
 * https://www.gz-yami.com/
 *
 * 未经允许，不可做商业用途！
 *
 * 版权所有，侵权必究！
 */

package com.yami.shop.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yami.shop.bean.app.dto.OrderCountData;
import com.yami.shop.bean.app.dto.ShopCartItemDto;
import com.yami.shop.bean.app.dto.ShopCartOrderMergerDto;
import com.yami.shop.bean.app.param.CreateOrderRequest;
import com.yami.shop.bean.app.param.PayParam;
import com.yami.shop.bean.app.param.ProductLine;
import com.yami.shop.bean.event.CancelOrderEvent;
import com.yami.shop.bean.event.ReceiptOrderEvent;
import com.yami.shop.bean.event.SubmitOrderEvent;
import com.yami.shop.bean.model.*;
import com.yami.shop.bean.param.OrderParam;
import com.yami.shop.bean.pay.PayInfoDto;
import com.yami.shop.common.exception.YamiShopBindException;
import com.yami.shop.common.util.PageAdapter;
import com.yami.shop.constant.ConstantValue;
import com.yami.shop.dao.*;
import com.yami.shop.factory.OrderFactory;
import com.yami.shop.service.*;
import ma.glasnost.orika.MapperFacade;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author lgh on 2018/09/15.
 */
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private SkuMapper skuMapper;

    @Autowired
    private OrderItemMapper orderItemMapper;

    @Autowired
    private ProductMapper productMapper;

    @Autowired
    private UserAddrOrderService userAddrOrderService;

    @Autowired
    private ProductService productService;

    @Autowired
    private SkuService skuService;

    @Autowired
    private ApplicationEventPublisher eventPublisher;

    @Autowired
    private OrderSettlementMapper orderSettlementMapper;

    @Autowired
    private PayService payService;

    @Autowired
    private AreaService areaService;

    @Autowired
    private DeliveryMapper deliveryMapper;

    @Autowired
    private NoticeMallService noticeMallService;



    @Override
    public Order getOrderByOrderNumber(String orderNumber) {
        return orderMapper.getOrderByOrderNumber(orderNumber);
    }

    @Override
    @CachePut(cacheNames = "ConfirmOrderCache", key = "#userId")
    public ShopCartOrderMergerDto putConfirmOrderCache(String userId, ShopCartOrderMergerDto shopCartOrderMergerDto) {
        return shopCartOrderMergerDto;
    }

    @Override
    @Cacheable(cacheNames = "ConfirmOrderCache", key = "#userId")
    public ShopCartOrderMergerDto getConfirmOrderCache(String userId) {
        return null;
    }

    @Override
    @CacheEvict(cacheNames = "ConfirmOrderCache", key = "#userId")
    public void removeConfirmOrderCache(String userId) {
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<Order> submit(String userId, ShopCartOrderMergerDto mergerOrder) {
        List<Order> orderList = new ArrayList<>();
        // 通过事务提交订单
        eventPublisher.publishEvent(new SubmitOrderEvent(mergerOrder, orderList));

        // 插入订单
        orderList.forEach(order -> orderMapper.insert(order));
        List<OrderItem> orderItems = orderList.stream().flatMap(order -> order.getOrderItems().stream()).collect(Collectors.toList());
        // 插入订单项，返回主键
        orderItemMapper.insertBatch(orderItems);


        return orderList;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String submit(CreateOrderRequest mergerOrder) {
        UserAddrOrder userAddrOrder = OrderFactory.createAddr(mergerOrder);
        //区
        Area area1 = areaService.getById(userAddrOrder.getAreaId());
        //市
        Area area2 = areaService.getById(userAddrOrder.getCityId());
        //省
        Area area3 = areaService.getById(userAddrOrder.getProvinceId());
        userAddrOrder.setProvince(getName(area3));
        userAddrOrder.setCity(getName(area2));
        userAddrOrder.setArea(getName(area1));
        //保存地址信息
        userAddrOrderService.save(userAddrOrder);
        Order order = OrderFactory.create(mergerOrder);
        order.setAddrOrderId(userAddrOrder.getAddrOrderId());
        order.setUserId(ConstantValue.USER);
        List<OrderItem> orderItems = new ArrayList<>();
        StringBuilder orderProdName = new StringBuilder(100);
        mergerOrder.getProducts().forEach(p->{
            //校验商品库存，同时扣除库存
            Sku sku = checkAndGetSku(p);
            Product product = checkAndGetProd(p,sku);
            OrderItem orderItem = new OrderItem();
            orderItem.setShopId(product.getShopId());
            orderItem.setOrderNumber(mergerOrder.getOrderId());
            orderItem.setProdId(sku.getProdId());
            orderItem.setSkuId(sku.getSkuId());
            orderItem.setSkuName(sku.getSkuName());
            orderItem.setProdCount(p.getNumber());
            orderItem.setProdName(sku.getProdName());
            orderItem.setPic(StrUtil.isBlank(sku.getPic()) ? product.getPic() : sku.getPic());
            orderItem.setPrice(product.getPrice());
            orderItem.setUserId(ConstantValue.USER);
            orderItem.setProductTotalAmount(0d);
            orderItem.setRecTime(new Date());
            orderItem.setCommSts(0);
            orderItem.setBasketDate(new Date());
            orderProdName.append(orderItem.getProdName()).append(",");
            orderItems.add(orderItem);

        });
        int sum = orderItems.stream().mapToInt(OrderItem::getProdCount).sum();
        order.setOrderItems(orderItems);
        order.setProdName(orderProdName.toString());
        order.setProductNums(sum);
        // 插入订单
        orderMapper.insert(order);
        // 插入订单项，返回主键
        orderItemMapper.insertBatch(orderItems);
        OrderSettlement orderSettlement = new OrderSettlement();
        orderSettlement.setUserId(ConstantValue.USER);
        orderSettlement.setIsClearing(0);
        orderSettlement.setCreateTime(new Date());
        orderSettlement.setOrderNumber(mergerOrder.getOrderId());
        orderSettlement.setPayAmount(order.getActualTotal());
        orderSettlement.setPayStatus(0);
        orderSettlement.setVersion(0);
        orderSettlementMapper.insert(orderSettlement);

        PayParam payParam = new PayParam();
        payParam.setOrderNumbers(mergerOrder.getOrderId());
        payParam.setPayType(0);
        //付款
        PayInfoDto pay = payService.pay(ConstantValue.USER, payParam);

        // 根据内部订单号更新order settlement
        payService.paySuccess(pay.getPayNo(), "");

        return order.getOrderId().toString();
    }

    private String getName(Area area){
        if(Objects.isNull(area)){
            return null;
        }
        return area.getAreaName();
    }


    private Product checkAndGetProd(ProductLine prod,Sku sku) {
        Product product = productService.getProductByProdId(sku.getProdId());
        if (product == null) {
            throw new YamiShopBindException("购物车包含无法识别的商品");
        }

        if (product.getStatus() != 1) {
            throw new YamiShopBindException("商品[" + product.getProdName() + "]已下架");
        }

        // -1为无限库存
        if (product.getTotalStocks() != -1 && prod.getNumber() > product.getTotalStocks()) {
            throw new YamiShopBindException("商品：[" + product.getProdName() + "]库存不足");
        }
        //不是无限库存就扣
        if (product.getTotalStocks() != -1) {
            product.setTotalStocks(prod.getNumber());
            productMapper.updateStocks(product);
        }
        return product;
    }

    private Sku checkAndGetSku(ProductLine prod) {
        Sku sku = skuService.getById(Long.valueOf(prod.getItemId()));
        if (Objects.isNull(sku)) {
            throw new YamiShopBindException("购物车包含无法识别的商品");
        }

        if (sku.getStatus() != 1) {
            throw new YamiShopBindException("商品[" + sku.getProdName() + "]已下架");
        }
        // -1为无限库存
        if (sku.getStocks() != -1 && prod.getNumber() > sku.getStocks()) {
            throw new YamiShopBindException("商品：[" + sku.getProdName() + "]库存不足");
        }
        //不是无限库存就扣
        if (sku.getStocks() != -1) {
            sku.setStocks(prod.getNumber());
            skuMapper.updateStocks(sku);
        }
        return sku;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delivery(Order order) {
        orderMapper.updateById(order);
        // 发送用户发货通知
        Map<String, Object> map = new HashMap<>(16);
		Delivery delivery = deliveryMapper.selectById(order.getDvyId());

        NoticeModel model = new NoticeModel();
        model.setNoticeId("1");
        model.setNoticeFlag("0");
        model.setNoticeType("10");
        map.put("order_sn","yami_"+order.getOrderId());
        map.put("express_name",delivery.getDvyName());
        map.put("express_code",order.getDvyFlowId());
        List<OrderItem> items = order.getOrderItems();
        map.put("itemId",items.get(0).getProdId());
        map.put("number",items.get(0).getProdCount());
        model.setCreatedStamp(new Date());
        model.setNoticeBody(JSONUtil.toJsonStr(map));

        noticeMallService.save(model);

    }

    @Override
    public List<Order> listOrderAndOrderItems(Integer orderStatus, DateTime lessThanUpdateTime) {
        return orderMapper.listOrderAndOrderItems(orderStatus, lessThanUpdateTime);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancelOrders(List<Order> orders) {

        orderMapper.cancelOrders(orders);
        List<OrderItem> allOrderItems = new ArrayList<>();
        for (Order order : orders) {
            noticeMallService.orderCloseNotice(order.getOrderId().toString());
            List<OrderItem> orderItems = order.getOrderItems();
            allOrderItems.addAll(orderItems);
            eventPublisher.publishEvent(new CancelOrderEvent(order));
        }
        if (CollectionUtil.isEmpty(allOrderItems)) {
            return;
        }
        Map<Long, Integer> prodCollect = new HashMap<>(16);
        allOrderItems.stream().collect(Collectors.groupingBy(OrderItem::getProdId)).forEach((prodId, orderItems) -> {
            int prodTotalNum = orderItems.stream().mapToInt(OrderItem::getProdCount).sum();
            prodCollect.put(prodId, prodTotalNum);
        });
        productMapper.returnStock(prodCollect);

        Map<Long, Integer> skuCollect = new HashMap<>(16);
        allOrderItems.stream().collect(Collectors.groupingBy(OrderItem::getSkuId)).forEach((skuId, orderItems) -> {
            int prodTotalNum = orderItems.stream().mapToInt(OrderItem::getProdCount).sum();
            skuCollect.put(skuId, prodTotalNum);
        });
        skuMapper.returnStock(skuCollect);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void confirmOrder(List<Order> orders) {
        orderMapper.confirmOrder(orders);
        for (Order order : orders) {
            noticeMallService.orderCloseNotice(order.getOrderId().toString());
            eventPublisher.publishEvent(new ReceiptOrderEvent(order));
        }

    }

    @Override
    public List<Order> listOrdersDetialByOrder(Order order, Date startTime, Date endTime) {
        return orderMapper.listOrdersDetialByOrder(order, startTime, endTime);
    }

    @Override
    public IPage<Order> pageOrdersDetialByOrderParam(Page<Order> page, OrderParam orderParam) {
        page.setRecords(orderMapper.listOrdersDetialByOrderParam(new PageAdapter(page), orderParam));
        page.setTotal(orderMapper.countOrderDetial(orderParam));
        return page;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteOrders(List<Order> orders) {
        orderMapper.deleteOrders(orders);
    }

    @Override
    public OrderCountData getOrderCount(String userId) {
        return orderMapper.getOrderCount(userId);
    }


}
